#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ArduinoJson.h>
#include <SD.h>
#include <SPI.h>
#include "PubSubClient.h"
#include <NTPClient.h>
#include <WiFiUdp.h>
#include <TimeLib.h>
/* const char *mqtt_server = "h3253k9765.oicp.vip";
const char *mqttUser = "lu";
const char *mqttPassWd = "5891381"; */
const int port = 1883;
const char *mqttID = "MQTTLogger";
char SSID[10];
char PassWd[20];
char recordname[30];
WiFiClient espClient;
WiFiUDP ntpUDP;
PubSubClient client(espClient);
// Our configuration structure.
struct Config
{
	char ssid[10];
	char PassWd[20];
	char mqtt_server[50];
	char mqttUser[10];
	char mqttPassWd[15];
	char topic[20];
};
NTPClient timeClient(ntpUDP, "ntp.aliyun.com", 8 * 3600, 60000);
struct dateTime
{
	int year;
	u_char month;
	u_char date;
	u_char day;
	u_char hour;
	u_char minute;
	u_char second;
};

dateTime timeSync()
{
	dateTime dt;
	unsigned long epochTime;
	timeClient.update();
	epochTime = timeClient.getEpochTime();
	dt.year = year(epochTime);
	dt.month = month(epochTime);
	dt.date = day(epochTime);
	dt.day = weekday(epochTime);
	dt.hour = hour(epochTime);
	dt.minute = minute(epochTime);
	dt.second = second(epochTime);
	return dt;
}
const char *filename = "/config.txt"; // <- SD library uses 8.3 filenames
Config config;						  // <- global configuration object
void printFile(const char *filename);
bool wifiConnect()
{
	if (WiFi.status() != WL_CONNECTED)
	{
		for (int i = 0; i < 20; i++)
		{
			if (WiFi.status() == WL_CONNECTED)
				break;
			delay(500);
			Serial.print(".");
		}
		if (WiFi.status() == WL_CONNECTED)
		{
			Serial.println("OK");
			Serial.print("IP address: ");
			Serial.println(WiFi.localIP());
			return 1;
		}
		else
		{
			Serial.println("WiFi connect failed");
			return 0;
		}
	}
	else
		return 1;
}
void callback(char *topic, byte *payload, unsigned int length)
{
	char msg[50];
	unsigned int i;
	dateTime dt;
	digitalWrite(2, 0);
	Serial.print("R[");
	Serial.print(topic);
	Serial.print("] ");
	for (i = 0; i < length; i++)
	{

		msg[i] = (char)payload[i];
	}
	msg[i] = '\0';
	Serial.println(msg);
	dt = timeSync();
	//	const char *recordname = "/record.txt";
	File recordfile = SD.open(recordname, FILE_WRITE); //,
	if (!recordfile)
	{
		Serial.println(F("Failed to create file"));
	}
	recordfile.printf("[%02d-%02d-%02d %02d:%02d:%02d]%s\n", dt.year - 2000, dt.month, dt.date, dt.hour, dt.minute, dt.second, msg);
	recordfile.close();
	digitalWrite(2, 1);
}
void subScribe()
{
	client.subscribe(config.topic);
}
void reconnect()
{
	wifiConnect();
	while (!client.connected())
	{
		Serial.print("Attempting MQTT connection...");
		if (client.connect(mqttID, config.mqttUser, config.mqttPassWd))
		{
			Serial.println("connected");
			subScribe();
		}
		else
		{
			Serial.print("failed, rc=");
			Serial.print(client.state());
			Serial.println(" try again in 5 seconds");
			delay(5000);
		}
	}
}
void initMQTT()
{
	/* 	uint8_t macAddr[6];
		WiFi.macAddress(macAddr);
		sprintf(mqttID, "MQTT-%X%X%X", macAddr[3], macAddr[4], macAddr[5]);
		 */
	client.setServer(config.mqtt_server, port);
	client.setCallback(callback);
	reconnect();
}
// Loads the configuration from a file
void loadConfiguration(const char *filename, Config &config)
{

	// Open file for reading
	File file = SD.open(filename);

	// Allocate a temporary JsonDocument
	JsonDocument doc;

	// Deserialize the JSON document
	DeserializationError error = deserializeJson(doc, file);
	if (error)
		Serial.println(F("Failed to read file, using default configuration"));

	// Copy values from the JsonDocument to the Config

	strlcpy(config.ssid,			// <- destination
			doc["ssid"] | "MiHome", // <- source
			sizeof(config.ssid));	// <- destination's capacity

	strlcpy(config.PassWd,				// <- destination
			doc["PassWd"] | "ftwc1982", // <- source
			sizeof(config.PassWd));		// <- destination's capacity
	strlcpy(config.topic,				// <- destination
			doc["topic"] | "test",		// <- source
			sizeof(config.topic));		// <- destination's capacity

	strlcpy(config.mqtt_server,							// <- destination
			doc["mqtt_server"] | "h3253k9765.oicp.vip", // <- source
			sizeof(config.mqtt_server));
	strlcpy(config.mqttUser,			   // <- destination
			doc["mqttUser"] | "lu",		   // <- source
			sizeof(config.mqttUser));	   // <- destination's capacity
	strlcpy(config.mqttPassWd,			   // <- destination
			doc["mqttPassWd"] | "5891381", // <- source
			sizeof(config.mqttPassWd));
	// Close the file (Curiously, File's destructor doesn't close the file)
	file.close();
}

// Saves the configuration to a file
void saveConfiguration(const char *filename, const Config &config)
{
	// Delete existing file, otherwise the configuration is appended to the file
	SD.remove(filename);

	// Open file for writing
	File file = SD.open(filename, FILE_WRITE);
	if (!file)
	{
		Serial.println(F("Failed to create file"));
		return;
	}

	// Allocate a temporary JsonDocument
	JsonDocument doc;

	// Set the values in the document
	doc["ssid"] = config.ssid;
	doc["PassWd"] = config.PassWd;
	doc["topic"] = config.topic;

	doc["mqtt_server"] = config.mqtt_server;
	doc["mqttUser"] = config.mqttUser;
	doc["mqttPassWd"] = config.mqttPassWd;
	// Serialize JSON to file
	if (serializeJson(doc, file) == 0)
	{
		Serial.println(F("Failed to write to file"));
	}

	// Close the file
	file.close();
}

// Prints the content of a file to the Serial
void printFile(const char *filename)
{
	// Open file for reading
	File file = SD.open(filename);
	if (!file)
	{
		Serial.println(F("Failed to read file"));
		return;
	}

	// Extract each characters by one by one
	while (file.available())
	{
		Serial.print((char)file.read());
	}
	Serial.println();

	// Close the file
	file.close();
}

void setup()
{
	// Initialize serial port
	Serial.begin(115200);
	while (!Serial)
		continue;
	delay(1000);
	pinMode(2, OUTPUT);
	Serial.println("\n-----------------");
	// Initialize SD library
	const int chipSelect = 15;
	while (!SD.begin(chipSelect))
	{
		Serial.println(F("Failed to initialize SD library"));
		delay(1000);
	}

	// Should load default config if run for the first time
	Serial.println(F("Loading configuration..."));
	loadConfiguration(filename, config);

	// Create configuration file
	Serial.println(F("Saving configuration..."));
	saveConfiguration(filename, config);

	// Dump config file
	Serial.println(F("Print config file..."));
	printFile(filename);
	SD.remove("/record.txt");
	WiFi.begin(config.ssid, config.PassWd);
	Serial.printf("Connect to %s", config.ssid);
	wifiConnect();
	initMQTT();
	timeClient.begin();
	dateTime dt;
	dt = timeSync();
	sprintf(recordname, "record-%02d%02d%02d%02d%02d%02d.txt", dt.year - 2000, dt.month, dt.date, dt.hour, dt.minute, dt.second);
	Serial.printf("Save topic \"%s\" message to \"%s\"\n", config.topic, recordname);
	for (int i = 0; i < 5; i++)
	{
		digitalWrite(2, 0);
		delay(100);
		digitalWrite(2, 1);
		delay(100);
	}
}

void loop()
{
	if (!client.connected())
	{
		reconnect();
	}
	client.loop();
}
